Skip to content

Fix duplicate check losing form params and nested attributes#1369

Merged
maebeale merged 8 commits intomainfrom
maebeale/fix-dupe-interstitial
Mar 9, 2026
Merged

Fix duplicate check losing form params and nested attributes#1369
maebeale merged 8 commits intomainfrom
maebeale/fix-dupe-interstitial

Conversation

@maebeale
Copy link
Copy Markdown
Collaborator

@maebeale maebeale commented Mar 8, 2026

Closes #1023

What is the goal of this PR and why is this important?

  • When creating a person/user triggers duplicate detection, all form data (especially nested attributes like sectors, addresses, phones, affiliations) was lost because the original implementation used redirect_to which discards POST params
  • The "Go back" button used history.back() which doesn't restore dynamically-added Cocoon fields
  • Also fixes a pre-existing bug where the "+ Add phone" button didn't work at all

How did you approach the change?

  • Replace redirect_to with render :check_duplicates to preserve POST params in controller state
  • Add hidden_fields_for_params helper to round-trip all form params (including nested attributes) through hidden fields on the interstitial page
  • Replace history.back() "Go back" with a retry_new POST action that rebuilds @person from params and re-renders the new form with nested objects pre-populated
  • Add retry_new collection route and controller action for both people and users
  • Fix "+ Add phone" cocoon bug: hidden kind field was outside the .nested-fields div, so cocoon's firstElementChild grabbed only the <input> and discarded the form fields
  • Add "Ask a colleague" mailto link on duplicate check interstitials
  • Add avatar warning when file upload can't be preserved through the round-trip
  • Skip blank id fields and UploadedFile objects in hidden field generation

UI Testing Checklist

  • Create person with duplicate name → interstitial shows with correct data
  • Click "Create anyway" → person created with sectors, addresses, phones, affiliations
  • Click "Go back" → form re-renders with all nested attributes preserved
  • Exact match (blocked) → only "Go back" shown, no "Create anyway"
  • "Ask a colleague" mailto link generates correct email
  • Avatar warning appears when photo was uploaded
  • "+ Add phone" button works on new person form
  • Create user with duplicate email → same interstitial flow works

Anything else to add?

  • Avatar files cannot be preserved through hidden fields (known limitation) — user sees a warning to re-upload
  • The cocoon phone button fix (moving hidden field inside .nested-fields div) is a separate commit that fixes a pre-existing bug

maebeale and others added 6 commits March 8, 2026 13:18
Replace redirect_to with render :check_duplicates so all POST params
stay available in the request. Add hidden_fields_for_params helper to
recursively render nested params as hidden inputs for the "Create
anyway" form. "Go back" link now navigates to the new form with params
to repopulate fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Passing nested params (sectors, addresses) via query string to the new
form didn't repopulate associations. Browser history.back() restores
the original form state including all fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Opens the user's email client with a pre-filled subject and the
shareable GET URL so a colleague can review the potential duplicates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skip file uploads (avatar) and blank id fields in hidden_fields_for_params
so nested attributes (affiliations, addresses, sectors, phones) are
preserved correctly through the Create anyway form. Show a note when
an avatar was uploaded that it will need to be re-added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cocoon's JS takes wrapper.firstElementChild from the template HTML.
The hidden kind field was before the .nested-fields div, so cocoon
only inserted the lone <input> and discarded the actual form fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace history.back() with POST to retry_new on both people and
  users duplicate check interstitials so nested attributes (sectors,
  addresses, phones, affiliations) are preserved when going back
- Remove debug logging from controllers
- Remove debug logging from cocoon JS
- Add explicit partial path for phone link_to_add_association
- Add request specs for retry_new and Create anyway with nested attrs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@maebeale maebeale changed the title Fix duplicate check losing form params Fix duplicate check losing form params and nested attributes Mar 8, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@maebeale maebeale requested a review from jmilljr24 March 8, 2026 18:56
@maebeale
Copy link
Copy Markdown
Collaborator Author

maebeale commented Mar 8, 2026

from claude:

I’d stick with the full render. 
The interstitial is a distinct decision point — “review these duplicates, then choose” — not an inline update to the form. 
Turbo Streams shine for partial page updates (flash messages, replacing a single row), 
but here the entire page content changes to a different layout (centered card vs. the form).

Also practical reasons:

- The “Go back” link navigates to new_person_path with params — a full page load either way
- The “Create anyway” form already uses data: { turbo: false } because it’s a standard POST that should redirect on success
- Adding Turbo Frames would mean wrapping the new form and the interstitial in matching frames, 
plus handling the layout difference — more complexity for no UX gain
- The render approach is the simplest thing that works correctly here.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@maebeale
Copy link
Copy Markdown
Collaborator Author

maebeale commented Mar 8, 2026

@jmilljr24 this is the "real" dupe check update

@jmilljr24
Copy link
Copy Markdown
Collaborator

When I "go back" Org affiliation name is displaying the id, not the name. Everything looked ok for Person.

I saw some code regarding avatars but I never got it to display in the UI. It sounds like if an avatar is added in the form, you lose it if there is a dup? Kinda of unfortunate but lower on the list of priorities as Admin at this point probably are not adding avatars for people, the person will do it themselves.

I'm good with this approach as is but I do think there is a better UX we could come up with in the future. Regarding the AI quote, it doesn't align with the flow/implementation I can envision. It's also confusing/mixing turbo frames vs turbo streams. I wouldn't dismiss other alternative idea based off that response.

@maebeale maebeale merged commit 1addded into main Mar 9, 2026
3 checks passed
@maebeale maebeale deleted the maebeale/fix-dupe-interstitial branch March 9, 2026 14:41
maebeale added a commit that referenced this pull request Mar 9, 2026
* Retain form params through duplicate check interstitial pages

Replace redirect_to with render :check_duplicates so all POST params
stay available in the request. Add hidden_fields_for_params helper to
recursively render nested params as hidden inputs for the "Create
anyway" form. "Go back" link now navigates to the new form with params
to repopulate fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use history.back() for Go back link on duplicate check pages

Passing nested params (sectors, addresses) via query string to the new
form didn't repopulate associations. Browser history.back() restores
the original form state including all fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add Ask a colleague mailto link on duplicate check pages

Opens the user's email client with a pre-filled subject and the
shareable GET URL so a colleague can review the potential duplicates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix nested attributes and avatar handling on duplicate check page

Skip file uploads (avatar) and blank id fields in hidden_fields_for_params
so nested attributes (affiliations, addresses, sectors, phones) are
preserved correctly through the Create anyway form. Show a note when
an avatar was uploaded that it will need to be re-added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix Add phone button by moving hidden field inside nested-fields div

Cocoon's JS takes wrapper.firstElementChild from the template HTML.
The hidden kind field was before the .nested-fields div, so cocoon
only inserted the lone <input> and discarded the actual form fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add retry_new action to preserve form data through Go back

- Replace history.back() with POST to retry_new on both people and
  users duplicate check interstitials so nested attributes (sectors,
  addresses, phones, affiliations) are preserved when going back
- Remove debug logging from controllers
- Remove debug logging from cocoon JS
- Add explicit partial path for phone link_to_add_association
- Add request specs for retry_new and Create anyway with nested attrs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix users dupe check spec: replace nonexistent trait with user: nil

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix users dupe check spec: eagerly create person to avoid count mismatch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants